package com.zeyuan.rpcclient.netty.helloworld01;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.*;
import java.util.Iterator;

public class SplitPiece {
    public static void main(String[] args) throws IOException {
        Selector selector = Selector.open();
        ServerSocketChannel ssc = ServerSocketChannel.open();
        ssc.configureBlocking(false);
        //建立selector和channel的联系(注册)

        SelectionKey register = ssc.register(selector, 0, null);
        register.interestOps(SelectionKey.OP_ACCEPT);
        ssc.bind(new InetSocketAddress(8888));
        while(true){ //如果数据量过大一次读取不下可能触发两次读取，但下面的bytebuf是局部变量，两次读取无法共享，如果放在while外面则多个channel会共享这个。这里就用到附件
            selector.select();
            Iterator<SelectionKey> iterator = selector.selectedKeys().iterator();
            while (iterator.hasNext()){

                SelectionKey next = iterator.next();
                iterator.remove();
                if(next.isAcceptable()){ //如果是连接事件
                    ServerSocketChannel channel = (ServerSocketChannel) next.channel();
                    SocketChannel accept = channel.accept();
                    accept.configureBlocking(false);
                    //selectionkey对应一个socketchannel，这里可以把bytebuf以附件形式注册到selectionkey上
                    ByteBuffer buffer = ByteBuffer.allocate(16);//讲bytebuffer附件形式注册到selecttionkey上
                    SelectionKey register1 = accept.register(selector, 0, buffer);
                    register1.interestOps(SelectionKey.OP_ACCEPT);


                }
                else if (next.isReadable()){
                    try {
                        SocketChannel channel = (SocketChannel) next.channel();
//                        ByteBuffer buffer = ByteBuffer.allocate(16); 这里字符串过多时多次while(true)，这个局部变量无法共享数据，写在外面会被多个channel共享，所以需要以附件形式注册到selectedKey上
                        //获取附件,也就是之前注册的bytebuffer，如果大小不够则attach()方法替换一个更大的附件
                        ByteBuffer buffer = (ByteBuffer) next.attachment();
                        int read = channel.read(buffer);
                        if (read==-1){ //客户端主动断开
                            next.cancel();
                        }else { //while(true)第二次触发读事件，因为数据太大一次读不下
                            //解析
                            split(buffer);
                            if(buffer.position()==buffer.limit()){ //说明没有内容被读取，compact没生效,容量满了
                                ByteBuffer newBuffer = ByteBuffer.allocate(buffer.capacity() * 2);
                                buffer.flip(); //读模式
                                newBuffer.put(buffer); //旧内容复制到新bytebuffer，从旧buffer读取内容到新buffer，旧buffer容量不够可能少东西，所以用新buffer替换
                                next.attach(newBuffer);

                            }
                        }
                    }catch (IOException e){
                        next.cancel(); //客户端被动断开
                    }
                }

            }
        }
    }
    //超出 bytebuffer大小的就新建一个更大的bytebuffer，原来的字符串 复制进去并放入新的字符串
    public static void split(ByteBuffer source){
        source.flip(); //切换成读取模式
        for(int i=0;i<source.limit();i++){ //limit代表下一个要读取或删除的位置
            if (source.get(i)=='\n'){
                int length = i + 1 - source.position();
                ByteBuffer buffer = ByteBuffer.allocate(length);
                for(int j=0;j<length;j++){
                    buffer.put(source.get());
                }

            }
        }
        source.compact(); //压缩，让position变成剩余未读字节数，让读取过的字节消失
    }
}
